#include "snmpinc.h"

#if SNMP_WANTED==1
static _UINT16 encodePointer=0;
/****************************************************************************
A_SizeOfSubId -- Compute the number of bytes required to hold a
                 subidentifier from an object id.
                 ASN.1 encoded (tag and length fields are not included)
Parameters: _UINT32
Returns: _UINT16  Number of octets needed in ASN.1 encoding
****************************************************************************/
#define A_SizeOfSubId(I)  (_UINT16)((_UINT32)(I) <= 0x7F ? 1 : ((_UINT32)(I) <= 0x3FFF ? 2 : ((_UINT32)(I) <= 0x1FFFFFL ? 3 : ((_UINT32)(I) <= 0x0FFFFFFFL ? 4 : 5))))

void A_EncodeStart()
{
	encodePointer=0;
}
/****************************************************************************
NAME:  A_EncodeHelper
PURPOSE:  Collect encoded data from the ASN.1 encoding routines and
          place it into a buffer.
PAREMETERS:
        EBUFFER_T * The "opaque" parameter (ebuffp) passed to the encoding
                    routines.
        _UINT8 *   The buffer where the encoded data resides
        _UINT16   The number of encoded octets in the buffer.
RETURNS:  Returns the number of octets consumed, zero is returned if
          no more data is desired.  (This may not, however, prevent
          subsequent calls.)
RESTRICTIONS: Can not handle length > 64K
BUGS:
****************************************************************************/
_UINT16 A_EncodeHelper(_UINT8 *ebp, _UINT8 *bp, _UINT16 leng)
{
	switch(leng)
	{
	case 0:
		break;
	case 1:
		ebp[encodePointer++] = *bp;
		break;
	case 2:
		ebp[encodePointer++] = *bp++;
		ebp[encodePointer++] = *bp;
		break;
	default:
		(void) memcpy(&ebp[encodePointer], bp, leng);
		encodePointer += (_UINT16) leng;
		break;
	}
	return (leng);
}


/****************************************************************************
A_SizeOfInt -- Return total size that an integer would occupy when
               ASN.1 encoded (tag and length fields are not included).
Parameters:       _INT32    The integer (signed 32 bit)
Returns: _UINT16  Number of octets the integer would occupy if in ASN.1 encoding
****************************************************************************/
_UINT16 A_SizeOfInt(_INT32 i)
{
	if(i >= 0L)
		return (i <= 0x0000007FL ? 1 :	/* <= 127       */
		        (i <= 0x00007FFFL ? 2 :	/* <= 32767     */
		         (i <= 0x007FFFFFL ? 3 :	/* <= 8388607   */
		          4)));			/* > 8388607    */
	else
		return (i >= (_INT32) 0xFFFFFF80L ? 1 :	/* >= -128      */
		        (i >= (_INT32) 0XFFFF8000L ? 2 :	/* >= -32768    */
		         (i >= (_INT32) 0XFF800000L ? 3 :	/* >= -8388608  */
		          4)));			/* < -8388608   */
}

/****************************************************************************
A_SizeOfUnsignedInt -- Return total size that an unsigned integer would
                       occupy when ASN.1 encoded (tag and length fields
                       are not included).
Parameters: _UINT32    The integer (unsigned 32 bit)
Returns: _UINT16  Number of octets the integer would occupy if in ASN.1 encoding
****************************************************************************/
_UINT16 A_SizeOfUnsignedInt(_UINT32 i)
{
	return (i <= 0x0000007FL ? 1 :	/* <= 127       */
	        (i <= 0x00007FFFL ? 2 :	/* <= 32768     */
	         (i <= 0x007FFFFFL ? 3 :	/* <= 8388607   */
	          (i <= 0x7FFFFFFFL ? 4 :	/* <= 2147483647 */
	           5))));			/* >  2147483647 */
}

/****************************************************************************
A_SizeOfObjectId -- Return total size that an object ID would occupy when
                ASN.1 encoded (tag and length fields are not included)
Parameters:    OBJ_ID_T *      Pointer to the internal object Id structure
Returns: _UINT16  Number of octets the object ID would occupy if in ASN.1 encoding
Note: It is assumed by this routine that the object identifier has at least two components.
****************************************************************************/
_UINT16 A_SizeOfObjectId(OBJ_ID_T *objp)
{
	_UINT16 leng;
	_UINT32 *cp = objp->component_list;
	_UINT16 i;
	_UINT32 x;
	if(objp->num_components == 0)
	{
		return 0;
	}
	/* Compute the value of the first subidentifier from the values of the  */
	/* first two components.                                                */
	{
		x = *cp++;
		x = x * 40 + *cp++;
		leng = A_SizeOfSubId(x);
	}
	for(i = 2; i < objp->num_components; i++)
	{
		x = *cp++;
		leng += A_SizeOfSubId(x);
	}
	return leng;
}

/****************************************************************************
A_SizeOfUnsignedInt64 -- Return total size that a big unsigned integer would
                         occupy when ASN.1 encoded (tag and length fields
                         are not included).
Parameters:       UINT64_T *   The integer (unsigned 64 bit)
Returns: _UINT16  Number of octets the integer would occupy if in ASN.1 encoding
****************************************************************************/
_UINT16 A_SizeOfUnsignedInt64(UINT64_T *i)
{
	if(i->high == 0)
		return (i->low <= 0x0000007FL ? 1 :	/* <= 127       */
		        (i->low <= 0x00007FFFL ? 2 :	/* <= 32768     */
		         (i->low <= 0x007FFFFFL ? 3 :	/* <= 8388607   */
		          (i->low <= 0x7FFFFFFFL ? 4 :	/* <= 2147483647 */
		           5))));		/* >  2147483647 */
	else
		return (i->high <= 0x0000007FL ? 5 :	/* <= 127       */
		        (i->high <= 0x00007FFFL ? 6 :	/* <= 32768     */
		         (i->high <= 0x007FFFFFL ? 7 :	/* <= 8388607   */
		          (i->high <= 0x7FFFFFFFL ? 8 :	/* <= 2147483647 */
		           9))));		/* >  2147483647 */
}
/****************************************************************************
A_EncodeType -- Encode an ASN.1 type field into buffer.
Parameters:
        _UINT16        The type value
        _UINT8          A_IDCF_MASK flag values
        _UINT16 (*f()) Function to be called to take generated data
        _UINT8 *        Parameter to be passed unchanged to the function.
Notes:  The function whose address is passed as a parameter is called zero
or more times to take away some accumulated data.  The function is called
with these parameters:
        _UINT8 *   The parameter (ebuffp) passed to this routine
        _UINT8 *   The buffer where the data resides
        _UINT16   The number of octets in the buffer.
The function should return the number of octets consumed, type _UINT16.
The function should return a zero if it has taken all the data it wants.
Returns: nothing
****************************************************************************/
void A_EncodeType(_UINT16 id, _UINT8 flags, _UINT8 *ebuffp)
{
	_UINT8 buff[5], reverse[4];	/* Can't handle more than 4 octets */
	_UINT8 *bp = buff;
	_UINT8 *rp = reverse;
	_UINT16 count = 0;	/* Should never exceed 4 */
	_UINT16 cnt;
	flags &= A_IDCF_MASK;
	if(id <= (_UINT16) 30)
	{
		_UINT8 c;
		c = flags | (_UINT8) id;
		(void)A_EncodeHelper(ebuffp, &c, (_UINT16) sizeof(_UINT8));
	}
	else
	{
		/* Build a partial reverse order version of the result and then */
		/* reverse it again back to correct order */
		*bp++ = (_UINT8)((flags & A_IDCF_MASK) | 0x1F);
		while(id > 0)
		{
			*rp++ = (_UINT8)(id & 0x7F);
			id >>= 7;
			count++;
		}
		cnt = count + 1;
		while((count--) > 1)
		{
			*bp++ = (_UINT8)(*(--rp) | 0x80);
		}
		*bp++ = *(--rp);
		(void)A_EncodeHelper(ebuffp, buff, cnt);
	}
}

/****************************************************************************
A_EncodeLength -- Encode an ASN.1 definite form length field into buffer.
Parameters:
        _UINT16        Length to be encoded
        _UINT16 (*f()) Function to be called to take generated data
        _UINT8 *        Parameter to be passed unchanged to the function.
Notes:  The function whose address is passed as a parameter is called zero
or more times to take away some accumulated data.  The function is called
with these parameters:
        _UINT8 *   The parameter (ebuffp) passed to this routine
        _UINT8 *   The buffer where the data resides
        _UINT16   The number of octets in the buffer.

The function should return the number of octets consumed, type _UINT16.
The function should return a zero if it has taken all the data it wants.
Returns: nothing
****************************************************************************/
void A_EncodeLength(_UINT16 leng,_UINT8 *ebuffp)
{
	_UINT8 buff[OCTETS_PER_INT32 + 1];
	_UINT8 reverse[OCTETS_PER_INT32];
	_UINT8 *bp = buff;
	_UINT8 *rp = reverse;
	_UINT16 count = 0;	/* Never exceeds OCTETS_PER_INT32 */
	_UINT16 cnt;
	if(leng <= (_UINT16) 127)
	{
		_UINT8 c;
		c = (_UINT8) leng;
		(void)A_EncodeHelper(ebuffp, &c, (_UINT16) sizeof(_UINT8));
	}
	else
	{
		while(leng > 0)
		{
			*rp++ = (_UINT8) leng;
			/*lint -e704    */
			leng >>= 8;
			/*lint +e704    */
			count++;
		}
		*bp++ = (_UINT8)(((_UINT8) count) | (_UINT8) 0x80);
		cnt = count + 1;
		while((count--) > 0)
		{
			*bp++ = *(--rp);
		}
		(void)A_EncodeHelper(ebuffp, buff, cnt);
	}
}

/****************************************************************************
A_EncodeInt -- generate ASN.1 format of integer (WITH TYPE & LENGTH)

Parameters:
    _UINT16        The type value
    _UINT8          A_IDC_MASK flag values
    _INT32         The integer to convert (signed 32 bit)
    _UINT16 (*f()) Function to be called to take generated data
    _UINT8 *        Parameter to be passed unchanged to the function.

Notes:  The function whose address is passed as a parameter is called zero
or more times to take away some accumulated data.  The function is called
with these parameters:
        _UINT8 *   The parameter (ebuffp) passed to this routine
        _UINT8 *   The buffer where the data resides
        _UINT16   The number of octets in the buffer.

The function should return the number of octets consumed, type _UINT16.
The function should return a zero if it has taken all the data it wants.

Returns: nothing

****************************************************************************/

void A_EncodeInt(_UINT16 id, _UINT8 flags, _INT32 value,_UINT8 *ebuffp)
{
	_UINT16 length;
	_UINT8 *rp;
	_UINT8 buff[OCTETS_PER_INT32];
	length = A_SizeOfInt(value);
	A_EncodeType(id, (_UINT8)(flags & A_IDC_MASK), ebuffp);
	A_EncodeLength(length,ebuffp);
	rp = buff + (_UINT16) length;
	for(;;)
	{
		*(--rp) = (_UINT8) value;
		if(rp == buff)
		{
			break;
		}
		/*lint -e704        */
		value >>= 8;
		/*lint +e704        */
	}
	(void)A_EncodeHelper(ebuffp, buff, length);
}

/****************************************************************************
A_EncodeUnsignedInt -- generate ASN.1 format of integer (WITH TYPE & LENGTH)
                       where the local form of the integer is unsigned.
Parameters:
    _UINT16        The type value
    _UINT8          A_IDC_MASK flag values
    _UINT32        The integer to convert (unsigned 32 bit)
    _UINT16 (*f()) Function to be called to take generated data
    _UINT8 *        Parameter to be passed unchanged to the function.

Notes:  The function whose address is passed as a parameter is called zero
or more times to take away some accumulated data.  The function is called
with these parameters:
        _UINT8 *   The parameter (ebuffp) passed to this routine
        _UINT8 *   The buffer where the data resides
        _UINT16   The number of octets in the buffer.
The function should return the number of octets consumed, type _UINT16.
The function should return a zero if it has taken all the data it wants.
Returns: nothing
****************************************************************************/
void A_EncodeUnsignedInt(_UINT16 id,_UINT8 flags, _UINT32 value, _UINT8 *ebuffp)
{
	_UINT16 leng, xleng;
	_UINT8 *rp;
	_UINT8 buff[OCTETS_PER_INT32 + 1];
	leng = A_SizeOfUnsignedInt(value);
	A_EncodeType(id, (_UINT8)(flags & A_IDC_MASK),ebuffp);
	A_EncodeLength(leng,ebuffp);
	rp = buff + (_UINT16) leng;
	/* If the unsigned number takes 5 octets, the high order octet is merely */
	/* a zero byte to hold the zero sign.                                    */
	for(xleng = leng; xleng--;)
	{
		*(--rp) = (_UINT8) value;
		value >>= 8;			/* This better be shifting zeros into the high end!   */
	}
	(void)A_EncodeHelper(ebuffp, buff, leng);
}

/****************************************************************************
A_EncodeOctetString -- Generate ASN.1 format of octet string (WITH TYPE & LENGTH)
Parameters:
    _UINT16        The type value
    _UINT8          A_IDC_MASK flag values
    _UINT8 *        Address of the string
    _UINT16        Length of the string
    _UINT16 (*f()) Function to be called to take generated data
    _UINT8 *        Parameter to be passed unchanged to the function.
Notes:  The function whose address is passed as a parameter is called zero
or more times to take away some accumulated data.  The function is called
with these parameters:
        _UINT8 *   The parameter (ebuffp) passed to this routine
        _UINT8 *   The buffer where the data resides
        _UINT16   The number of octets in the buffer.
The function should return the number of octets consumed, type _UINT16.
The function should return a zero if it has taken all the data it wants.
****************************************************************************/
void A_EncodeOctetString(_UINT16 id,_UINT8 flags,
                         _UINT8 *osp, _UINT16 oslen,_UINT8 *ebuffp)
{
	/* Do a primitive encoding */
	if(oslen>MAX_OCTET_LENGTH)
	{
		oslen=MAX_OCTET_LENGTH;
	}
	A_EncodeType(id, (_UINT8)(flags & A_IDC_MASK), ebuffp);
	A_EncodeLength(oslen, ebuffp);
	if(oslen != 0)
	{
		A_EncodeHelper(ebuffp, osp, oslen);
	}
}

/****************************************************************************
A_EncodeSubId -- generate ASN.1 format of a subidentifier from an
                 object identifier
Parameters:
    _UINT32      The subidentifier to encode
    _UINT16 (*f())    Function to be called to take generated data
    _UINT8 *           Parameter to be passed unchanged to the function.
Notes:  The function whose address is passed as a parameter is called zero
or more times to take away some accumulated data.  The function is called
with these parameters:
        _UINT8 *   The parameter (ebuffp) passed to this routine
        _UINT8 *   The buffer where the data resides
        _UINT16   The number of octets in the buffer.
The function should return the number of octets consumed, type _UINT16.
The function should return a zero if it has taken all the data it wants.
Returns: nothing
****************************************************************************/
void A_EncodeSubId(_UINT32 value,_UINT8 *ebuffp)
{
	_UINT16 leng;
	_UINT8 *rp;
	_UINT8 buff[OCTETS_PER_INT32 + 1];
	_UINT8 last;
	leng = A_SizeOfSubId(value);
	for(rp = buff + leng, last = 0x00; rp != buff;)
	{
		*(--rp) = (_UINT8)(value & 0x007F) | last;
		value >>= 7;
		last = 0x80;
	}
	(void)A_EncodeHelper(ebuffp, buff, leng);
}

/****************************************************************************
A_EncodeObjectId -- generate ASN.1 format of Object ID (WITH TYPE & LENGTH)
Parameters:
    _UINT16        The type value
    _UINT8          A_IDC_MASK flag values
    OBJ_ID_T *       Pointer to the internal object Id structure
    _UINT16 (*f()) Function to be called to take generated data
    _UINT8 *        Parameter to be passed unchanged to the function.

Notes:  The function whose address is passed as a parameter is called zero
or more times to take away some accumulated data.  The function is called
with these parameters:
        _UINT8 *   The parameter (ebuffp) passed to this routine
        _UINT8 *   The buffer where the data resides
        _UINT16   The number of octets in the buffer.

The function should return the number of octets consumed, type _UINT16.
The function should return a zero if it has taken all the data it wants.

Returns: nothing

****************************************************************************/
void A_EncodeObjectId(_UINT16 id, _UINT8 flags, OBJ_ID_T *objp, _UINT8 *ebuffp)
{
	_UINT16 leng;
	_UINT32 *cp = objp->component_list;
	_UINT16 i;
	_UINT32 x;
	leng = A_SizeOfObjectId(objp);
	A_EncodeType(id, (_UINT8)(flags & A_IDC_MASK), ebuffp);
	A_EncodeLength(leng, ebuffp);
	if(leng == 0)
	{
		return;
	}
	/* Merge the first two components of the object identifier to form the  */
	/* first subidentifier.                                                 */
	{
		x = *cp++;
		x = x * 40 + *cp++;
		A_EncodeSubId(x,  ebuffp);
	}
	for(i = 2; i < objp->num_components; i++)
	{
		A_EncodeSubId(*cp++,ebuffp);
	}
}

/* We only inlcude the ui64 function if the type is installed */
/****************************************************************************
A_EncodeUnsignedInt64 -- generate ASN.1 format of integer (WITH TYPE & LENGTH)
                         where the local form of the integer is unsigned 64.
Parameters:
    _UINT16        The type value
    _UINT8          A_IDC_MASK flag values
    UINT64_T *      The integer to convert (unsigned 64 bit)
    _UINT16 (*f()) Function to be called to take generated data
    _UINT8 *        Parameter to be passed unchanged to the function.
Notes:  The function whose address is passed as a parameter is called zero
or more times to take away some accumulated data.  The function is called
with these parameters:
        _UINT8 *   The parameter (ebuffp) passed to this routine
        _UINT8 *   The buffer where the data resides
        _UINT16   The number of octets in the buffer.

The function should return the number of octets consumed, type _UINT16.
The function should return a zero if it has taken all the data it wants.

Returns: nothing

****************************************************************************/
void A_EncodeUnsignedInt64(_UINT16 id,_UINT8 flags, UINT64_T *value, _UINT8 *ebuffp)
{
	_UINT16 leng, xleng;
	_UINT8 *rp;
	_UINT8 buff[(OCTETS_PER_INT32 * 2) + 1];
	memset(buff,0,sizeof(buff));
	leng = A_SizeOfUnsignedInt64(value);
	A_EncodeType(id, (_UINT8)(flags & A_IDC_MASK),ebuffp);
	A_EncodeLength(leng, ebuffp);
#if 1
	rp = (_UINT8 *) &buff[leng];
	/* If the unsigned number takes 9 octets, the high order octet is merely */
	/* a zero byte to hold the zero sign.                                    */
	if(leng <= 4)
	{
		for(xleng = leng; xleng>0; xleng--)
		{
			*(--rp) = (_UINT8) value->low;
			value->low >>= 8;
		}
	}
	else
	{
		for(xleng = 4; xleng>0; xleng--)
		{
			*(--rp) = (_UINT8) value->low;
			value->low >>= 8;
		}
		for(xleng = leng - 4; xleng>0; xleng--)
		{
			*(--rp) = (_UINT8) value->high;
			value->high >>= 8;
		}
	}
#endif
	(void)A_EncodeHelper(ebuffp, buff, leng);
}
#endif
